#ifndef __POLYGON_H__
#define __POLYGON_H__

#pragma once

#include "Vertex.h"
#include "GLAddon\PsudoColorRGB.h"
#include "Math\mathlib.h"
#include "Math\matrix.h"
#include <numeric>
#define		MINMOVE			5		// the min move distance for drag handles to be zero

#define		PIXELERROR		3		// the error for point selecting

#define		ZOOMVALUE		1.1		// the zooming value
#define		GRIDOFFSET		10		// the grid offset for dense or sparse

// the default color: set to be Black
#define		DefaultColor	RGB(0,0,0)

// colors of drawing different elements
enum DrawHostColor
{
	DHC_BKGROUND,				// color of background
	DHC_LOGCOLOR,				// color of the log
	DHC_VERTEX,					// color of drawing vertices of the polygon
	DHC_SELECTED_VERTEX,		// color of selected vertex in MODIFY operation
	DHC_SELECTED_POLYGON,		// color of the edges of selected polygons
	DHC_HANDLES,				// color of the handles of polygon
	DHC_ANCHOR,					// color of the anchor of polygon
	DHC_BOUNDINGBOX,			// color of the bounding box of polygon
	DHC_NEWINPUTPOLYGON,		// color of the edges of new input polygon
	DHC_GRIDLINE,				// color of the grid lines
	DHC_RUBBER,					// color of the rubber lines
	DHC_FILLCOLOR,				// color of filling the closed polygon
	DHC_EDGECOLOR,				// color of the polygon's edges
	DHC_AXIS,					// color of the axis
	DHC_SIZE,					// number
	DHC_INVALID					// invalid index
};

/*

// colors of drawing different elements
static	COLORREF	g_Colors[DHC_SIZE] =
{
	RGB(245,255,255),		// DHC_BKGROUND: color of background
	RGB(0,0,150),			// DHC_LOGCOLOR: color of the log
	RGB(255,0,0),			// DHC_VERTEX: color of drawing vertices of the polygon
	RGB(0,0,255),			// DHC_SELECTED_VERTEX: color of selected vertex in MODIFY operation
	RGB(0,0,255),			// DHC_SELECTED_POLYGON: color of the edges of selected polygons
	RGB(0,255,0),			// DHC_HANDLES: color of the handles of polygon
	RGB(255,0,255),			// DHC_ANCHOR: color of the anchor of polygon
	RGB(100,250,20),		// DHC_BOUNDINGBOX: color of the bounding box of polygon
	RGB(0,100,250),			// DHC_NEWINPUTPOLYGON: color of the edges of new input polygon
	RGB(0,50,50),			// DHC_GRIDLINE: color of the grid lines
	RGB(0,0,0),				// DHC_RUBBER: color of the rubber lines
	RGB(200,250,250),		// DHC_FILLCOLOR: color of the closed polygon filling
	RGB(0,0,0)				// DHC_EDGECOLOR: color of the polygon' edges
};

// sizes of drawing different elements
static	unsigned int	g_Sizes[DHC_SIZE] =
{
	0,			// DHC_BKGROUND: no use here...
	11,			// DHC_LOGCOLOR: width of the font
	2,			// DHC_VERTEX: point size of drawing vertices of the polygon
	3,			// DHC_SELECTED_VERTEX: point size of selected vertex in MODIFY operation
	2,			// DHC_SELECTED_POLYGON: line width of the edges of selected polygons
	3,			// DHC_HANDLES: point size of the handles of polygon
	4,			// DHC_ANCHOR: point size of the anchor of polygon
	1,			// DHC_BOUNDINGBOX: line width of the bounding box of polygon
	3,			// DHC_NEWINPUTPOLYGON: line width of the edges of new input polygon
	1,			// DHC_GRIDLINE: line width of the grid lines
	1,			// DHC_RUBBER: line width of the rubber lines
	0,			// DHC_FILLCOLOR:  no use here...
	1			// DHC_EDGECOLOR:  line width for drawing the polyon's boundary edges
};
*/

// the selected mode for handles
enum HandleMode
{
	POLYHNDL_UPPERLEFT,
	POLYHNDL_LEFT,
	POLYHNDL_LOWERLEFT,
	POLYHNDL_BOTTOM,
	POLYHNDL_LOWERRIGHT,
	POLYHNDL_RIGHT,
	POLYHNDL_UPPERRIGHT,
	POLYHNDL_UPPER,
	POLYHNDL_ANCHOR,		// the anchor point, it is the anchor point for all the transformations
	POLYHNDL_NUMBER,	// number of the handles
	POLYHNDL_INSIDE,		// in the region of the polygon
	POLYHNDL_OUTSIDE		// out of the region of the polygon
};


// class of polygon
class CPolygon
{
private:
	vertex_list m_vecVertex;		// the vertex list
	bool m_bClosed;					// whether the polygon is closed or not

	CVertex		m_Handles[POLYHNDL_NUMBER];	// handles

	BOOL		m_bFilled;			// whether the polygon is filled or not
	COLORREF	m_FillColor;		// the filled color for this polygon

	BOOL		m_bLined;			// whether draw the polygon lines
	COLORREF	m_LineColor;		// the line color
	UINT		m_nLineWidth;		// the line width
	std::vector<Vector2> Normal;
public:
	CPolygon(void);
	CPolygon(CPolygon &polygon);
	CPolygon& operator = (CPolygon &polygon);
	virtual ~CPolygon(void);

	void		Clear();
	double XMin() { return m_Handles[ POLYHNDL_UPPERLEFT  ].x; }
	double YMin() { return m_Handles[ POLYHNDL_UPPERLEFT  ].y; }
	double XMax() { return m_Handles[ POLYHNDL_LOWERRIGHT ].x; }
	double YMax() { return m_Handles[ POLYHNDL_LOWERRIGHT ].y; }

	void		Assign(vertex_list &list);	// convert a vertex_list instance to CPolygon instance
	int			NumVertex();    	// get count of vertices
	CVertex& operator [](int index);		// get a Vertex by index
	vertex_list	&GetVertexList();	// get the vertex list
	void		AddVertex(CVertex *vertex);		// add a Vertex to the end
	void		AddVertex(double x, double y);
	void		AddVertex(int x, int y);
	void		InsertAt( int index, CVertex *pV );	// insert a vertex at some index
	CVertex		*GetVertex(int Index);
	void		RemoveAt( int index );	// remove a vertex at some index
	void		RemoveLast();
	void		Reverse();
	CPolygon	*Split( int idx );
	void		ResetOrder( int idx );
	void		Resample( int nNumVertices, BOOL bKeepOrgVertices );

	void		SetAnchor( int idx );
	void		SetAnchorAsFirstVertex();
	void		SetAnchor( double x, double y );
	void		SetAnchorAsHandle(HandleMode mode);
	void		SetAnchorAsCenter();

	bool		GetClosed();		// return true for closed polygon, false otherwise (polyline)
	void		SetClosed();		// set the polygon to be closed
	void		SetClosed(bool close);
	void		SetNonClosed();	// set the polygon to be non-closed

	void		SetFilled( BOOL filled ) { m_bFilled = filled; }
	BOOL		GetFilled()	{ return m_bFilled; }
	void		SetFilledColor( COLORREF color ) { m_FillColor = color; }
	COLORREF	GetFilledColor()	{ return m_FillColor; }
	void		SetLined( BOOL lined )	{ m_bLined = lined; }
	BOOL		GetLined()	{ return m_bLined; }
	void		SetLinedColor( COLORREF color )		{ m_LineColor = color; }
	COLORREF	GetLinedColor()  { return m_LineColor; }
	void		SetLineWidth(int width ) { m_nLineWidth = width; }
	int			GetLineWidth()	{ return m_nLineWidth; }

	void		UpdateChanges();
	void		ComputeBoundingBox();
	void		ComputeHandles();
	void		ComputeAnchor();

	void		Save( const char *fname );
	void		Save( FILE *fp );

	// drawing procedures
	void		Draw( CDC *pDC, CPen *psetpen=NULL );
	void		Draw( CDC *pDC, BOOL bColoredEdge );
	//void		DrawUsePen( CDC *pDC, CPen &pen );
	void		DrawVertices( CDC *pDC );
	void		DrawBoundingBox( CDC *pDC );
	void		DrawHandles( CDC *pDC );
	void		ShowVertexIndices( CDC *pDC );

	int			PolyContainPoint( double px, double py );
	int			PolyVertexOnPoint( double px, double py );
	int			PolyVertexOnEdge( double &ratio, double px, double py );
	HandleMode	PointHandleMode( double px, double py );
	void		SubEdge( int idx, int nSeg );
	void		SubAllEdge( int nSeg );

	// transformations
	void		Translate( double px, double py );
	void		TranslateAnchor( double px, double py );
	double		GetScaleXScale( double x0, double x1 );
	void		ScaleX( double scale );
	void		ScaleX( double scale, double xorg );
	void		ScaleMoveX( double x0, double x1 );
	double		GetScaleYScale( double y0, double y1 );
	void		ScaleY( double scale );
	void		ScaleY( double scale, double yorg );
	void		Scale( double scaleX, double scaleY );
	void		Scale( double scaleX, double scaleY, double xorg, double yorg );
	void		ScaleMoveY( double y0, double y1 );
	void		ScaleMove( double x0, double y0, double x1, double y1 );
	void		ScaleMoveUnified( double x0, double y0, double x1, double y1 );

	double		GetRotateAngle( double x0, double y0, double x1, double y1 );
	void		Rotate( double angle );
	void		Rotate( double angle, double xorg, double yorg );
	void		RotateMove( double x0, double y0, double x1, double y1 );

	double		GetShearXScale( double x0, double y0, double x1, double y1 );
	void		ShearX( double scale, double yorg );
	void		ShearX( double scale );
	void		ShearMoveX( double x0, double y0, double x1, double y1 );
	double		GetShearYScale( double x0, double y0, double x1, double y1 );
	void		ShearY( double scale, double xorg );
	void		ShearY( double scale );
	void		ShearMoveY( double x0, double y0, double x1, double y1 );

	void		ReverseOrientation();
	void		FlipVertical( double ybase );
	void		FlipVertical();
	void		FlipHorizontal( double xbase );
	void		FlipHorizontal();


	CVertex		GetHandle( HandleMode mode );
	CPolygon	*Clone();
	void		Zoom( double ratio );

	//void DelaunayTriangulation(CString fname,double transX=0, double flipY=0,double AvgTriArea=100.0,bool bAddFrameBorder=false, float frameWidth=0.0, float frameHeight=0.0);
	//## added by 09/03/2003
	void		SmoothLaplacian(double ratio=0.1f, int iternum=5);	// Laplacian smoothing for polygons
	void		FourPointSubdivision(double ratio=0.125f, int iternum=1);			// 4-point subdivision
	void		PolygonEvolution(double ratio=0.1f, int iternum=1, BOOL bRestrained=false, double fRestrainedRatio=0.1f, BOOL bReverse=false);			// polygon evolution

	void		Simplification(BOOL abs_num, int rm_num, double rm_ratio);		// polygon simplification
	//## e.o.m. by 09/03/2003

	void DrawInGL(CSize window_size, float fGLViewWidth, float fGLViewHeight, bool bDrawCurve, bool bDrawVertex,bool bDrawNormal,CPseudoColorRGB &PsdC,Vector2 Origin=Vector2(0,0));
	void		DrawVerticesInGL( );
	void		GetAllPointsXY(std::vector<float> &dstX, std::vector<float> &dstY){
		int nV= NumVertex();
		dstX.clear();	dstX.resize(nV); 
		dstY.clear();	dstY.resize(nV);
		for (int i=0; i<nV; ++i){
			CVertex *pV = GetVertex(i);			dstX[i] = pV->x; dstY[i] = pV->y;
		}
	}
	void		GetAllNormalV2(std::vector<Vector2> &dst){
		int nV= NumVertex();
		dst.clear();	dst.resize(nV); 
		for (int i=0; i<nV; ++i){
			CVertex *pV = GetVertex(i);			dst[i]=Normal[i];
		}
	}
	void GetAllSegLength(std::vector<double> &dst){
		int nV= NumVertex();
		dst.clear();	dst.resize(nV-1); 
		for (int i=0; i<nV-1; ++i){
			CVertex *pV0 = GetVertex(i);  CVertex *pV1 = GetVertex(i+1);
			dst[i] = sqrt(	
				(pV0->x - pV1->x)* (pV0->x - pV1->x)+(pV0->y - pV1->y)* (pV0->y - pV1->y));			
		}
	}
	void GetAllSegSquaredLength(std::vector<double> &dst){
		int nV= NumVertex();
		dst.clear();	dst.resize(nV-1); 
		for (int i=0; i<nV-1; ++i){
			CVertex *pV0 = GetVertex(i);  CVertex *pV1 = GetVertex(i+1);
			dst[i] = (pV0->x - pV1->x)* (pV0->x - pV1->x)
					+(pV0->y - pV1->y)* (pV0->y - pV1->y);
		}
	}
	void GetAllSegSquaredLength(std::vector<double> &dst,double *ratio){
		int nV= NumVertex();
		dst.clear();	dst.resize(nV-1); 
		for (int i=0; i<nV-1; ++i){
			CVertex *pV0 = GetVertex(i);  CVertex *pV1 = GetVertex(i+1);
			dst[i] = (pV0->x - pV1->x)* (pV0->x - pV1->x)*ratio[0]
					+(pV0->y - pV1->y)* (pV0->y - pV1->y)*ratio[1];
		}
	}
	void ResampleByAvgEdgeLen(double Avglen){
		int nV= NumVertex();
		std::vector<double> len;
		GetAllSegLength(len);
		double total_len = std::accumulate(len.begin(), len.end(),0.0);
		int np = int (total_len/Avglen + 0.5);
		Resample(np,false);
	}
	void VertexNormal();
	void DrawNormal(CDC *pDC);
};

#endif
